﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;

namespace SuperFrame.Charts.Axis
{
    public class AxisBase : Canvas
    {

        internal virtual void InvalidateVisuals()
        {
        }

        protected Line axisLine;
        protected List<ContentControl> labels;
        protected List<Line> majorTickLines;
        protected List<Line> minorTickLines;
        protected ContentControl header;
        protected bool isInitialized;
        private bool isIntervalCountZero;

        private ActualType actualType;
        internal ActualType ActualType
        {
            get { return actualType; }
            set { actualType = value; }
        }

        public AxisBase()
        {
            this.m_Labels = new List<string>();
            this.ActualType = ActualType.Double;
        }


        /// <summary>
        /// Axis最小值
        /// </summary>
        public object MinValue
        {
            get { return (object)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }

        public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register("MinValue", typeof(object), typeof(AxisBase), new PropertyMetadata(null, OnMinValueChanged));
        private static void OnMinValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            (sender as AxisBase).MinValueChnaged(args);
        }
        internal void MinValueChnaged(DependencyPropertyChangedEventArgs args)
        {
            this.m_MinValue = Double.Parse(this.MinValue.ToString());
            this.m_startValue = Double.Parse(this.MinValue.ToString());
            if (this.Chart != null && this.Chart.containers != null)
                Chart.containers.Refresh();
            RefreshSeries();
        }

        /// <summary>
        /// Axis最大值 
        /// </summary>
        public object MaxValue
        {
            get { return (object)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }
        public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(object), typeof(AxisBase), new PropertyMetadata(null, OnMaxValueChanged));
        private static void OnMaxValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            (sender as AxisBase).MaxValueChnaged(args);
        }
        internal void MaxValueChnaged(DependencyPropertyChangedEventArgs args)
        {
            this.m_MaxValue = Double.Parse(this.MaxValue.ToString());
            if (this.Chart != null && this.Chart.containers != null)
                Chart.containers.Refresh();
            RefreshSeries();
        }

        /// <summary>
        /// 时间轴
        /// </summary>
        public object Interval
        {
            get { return (object)GetValue(IntervalProperty); }
            set { SetValue(IntervalProperty, value); }
        }
        public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register("Interval", typeof(object), typeof(AxisBase), new PropertyMetadata(null, OnIntervalChanged));
        private static void OnIntervalChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            (sender as AxisBase).IntervalChanged(args);
        }
        internal void IntervalChanged(DependencyPropertyChangedEventArgs args)
        {
            switch (ActualType)
            {
                case ActualType.Double:
                case ActualType.String:
                    // DateTime time = new DateTime();
                    if (!this.Interval.ToString().Contains(":"))
                        this.m_Interval = Double.Parse(this.Interval.ToString());
                    else
                        this.m_Interval = (DateTime.Now + (TimeSpan)TimeSpan.Parse(Interval.ToString())).ToOADate() - DateTime.Now.ToOADate();
                    break;
                case ActualType.DateTime:
                    this.m_Interval = (DateTime.Now + (TimeSpan)TimeSpan.Parse(Interval.ToString())).ToOADate() - DateTime.Now.ToOADate();
                    break;
                default:
                    break;
            }

            if (this.Chart != null && this.Chart.containers != null)
            {
                Chart.containers.Refresh();
                Chart.containers.axisLinesconatiner.Refresh();
            }
            RefreshSeries();
        }



        /// <summary>
        /// 给Axis添加最大最小值
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        public void AddMinMax(double min, double max)
        {
            if (this.MaxValue == null)
                m_MaxValue = max;
            if (this.MinValue == null)
            {
                m_MinValue = min;
                if (!isStartSet)
                {
                    m_startValue = min;
                    isStartSet = true;
                }
            }

            if (m_MaxValue == m_MinValue)
            {
                var tmp = this.m_Interval * 5;
                if (m_MaxValue == 0)
                    m_MaxValue = 1;
                m_MinValue = m_MinValue - tmp;
            }
            if (min >= 0 && m_MinValue < 0)
                m_MinValue = 0;
        }
        public void AddMinMax(double min, double max, double tmp)
        {
            if (min == max)
            {
                if (min == 0)
                {
                    max = 1;
                }
                else
                {
                    min -= min / 2;
                    max += min / 2;
                }
            }
            double x;
            if (!string.IsNullOrEmpty(this.StringFormat) && double.TryParse(1.ToString(this.StringFormat), out tmp))
            {
                var strFormat = (Convert.ToDouble(this.StringFormat + "1") * 10) > 1 ? 1 : (Convert.ToDouble(this.StringFormat + "1") * 10);
                x = Math.Abs(Convert.ToDouble(((max - min) / (IntervalCount - 1)).ToString(this.StringFormat)));
                x = x == 0 ? strFormat : x;
                m_MinValue = Math.Floor(min * (1 / strFormat)) / (1 / strFormat);
                m_MaxValue = m_MinValue + Convert.ToDouble((IntervalCount * x).ToString(this.StringFormat));
                if (m_MaxValue <= max)
                {
                    x += strFormat;
                    m_MinValue = Math.Floor(min * (1 / strFormat)) / (1 / strFormat);
                }
            }
            else
            {
                x = Math.Abs((max - min) / (IntervalCount - 1));
                m_MinValue = Math.Floor(min * 100) / 100;
            }
            m_MaxValue = m_MinValue + Convert.ToDouble((IntervalCount * x).ToString(this.StringFormat));
        }

        /// <summary>
        /// Add Interval to Axis
        /// </summary>
        /// <param name="interval">Interval to be added for Axis</param>
        public void AddInterval(double interval)
        {
            if (this.Interval == null)
                m_Interval = interval;
        }

        internal void GenerateLabels()
        {
            m_Labels.Clear();
            double value = m_MinValue;
            for (int i = 0; i <= IntervalCount; i++)
            {
                if (value < m_MinValue)
                {
                    value += m_Interval;
                    continue;
                }
                m_Labels.Add(GetOriginalLabel(value));
                value += m_Interval;
                if (isIntervalCountZero)
                    break;
            }
        }

        protected bool IsRectIntersect(Rect source, Rect relativeTo)
        {
            return !(relativeTo.Left > source.Right || relativeTo.Right < source.Left || relativeTo.Top > source.Bottom || relativeTo.Bottom < source.Top);
        }


        public virtual double DataToPoint(double value)
        {
            return 0;
        }

        public virtual void CalculateIntervalFromSeriesPoints()
        {

        }
        public virtual string GetOriginalLabel(double value)
        {
            return string.Empty;
        }

        protected void CalculateAutoInterval()
        {
            isIntervalCountZero = false;
            if (CheckType())
            {
                if (this.Interval == null)
                {
                    switch (ActualType)
                    {
                        case ActualType.Double:
                            //m_MinValue = Math.Floor(m_MinValue);                           
                            //  this.m_Interval = AxisUtil.CalculateInetrval(Math.Floor(m_MinValue), Math.Ceiling(m_MaxValue), IntervalCount);
                            this.m_Interval = Math.Abs((m_MaxValue - m_MinValue) / IntervalCount);
                            //m_MaxValue = m_MinValue + (IntervalCount * this.m_Interval);
                            break;
                        case ActualType.DateTime:
                            this.m_Interval = (m_MaxValue - m_MinValue) / IntervalCount;
                            break;
                        default:
                            break;
                    }

                    // this.m_Interval = (m_MaxValue - m_MinValue) / IntervalCount;
                }
                else
                {
                    switch (ActualType)
                    {
                        case ActualType.Double:
                            this.IntervalCount = (int)Math.Abs((m_MaxValue - m_MinValue) / m_Interval);
                            double temp_max = (this.IntervalCount * this.m_Interval) + m_MinValue;
                            if (temp_max >= this.m_MaxValue)
                                this.m_MaxValue = temp_max;
                            if (temp_max < this.m_MaxValue)
                            {
                                this.m_MaxValue = temp_max + this.m_Interval;
                                IntervalCount++;
                            }

                            break;
                        case ActualType.DateTime:
                            this.IntervalCount = (int)Math.Abs((m_MaxValue - m_MinValue) / m_Interval);
                            break;
                        default:
                            break;
                    }
                    if (IntervalCount <= 1)
                        isIntervalCountZero = true;
                    IntervalCount = (IntervalCount > 0) ? IntervalCount : 1;

                }
                //if ((m_MinValue >= m_startValue + m_Interval))
                //    m_startValue = m_MinValue + (m_MinValue % m_Interval);
                m_startValue = m_MinValue;
            }
            else
            {
                if (this.Interval == null)
                {
                    this.m_Interval = 1;
                    m_startValue = m_MinValue;
                }
                else
                {
                    IntervalCount = (int)(((int)m_MaxValue - (int)m_MinValue) / (int)m_Interval);
                    double temp_max = (this.IntervalCount * this.m_Interval) + m_MinValue;
                    if (temp_max >= this.m_MaxValue)
                        this.m_MaxValue = temp_max;
                    else
                    if (temp_max < this.m_MaxValue)
                    {
                        this.m_MaxValue = temp_max + this.m_Interval;
                        IntervalCount++;
                    }
                    if ((m_MinValue >= m_startValue + m_Interval))
                        m_startValue = m_startValue + m_Interval;
                }
                this.IntervalCount = (int)Math.Abs(((int)m_MaxValue - (int)m_MinValue) / (int)m_Interval);
                IntervalCount = (IntervalCount > 0) ? IntervalCount : 1;

            }

            RefreshSeries();
            if (this.Chart.containers != null && this.Chart.containers.axisLinesconatiner != null)
                this.Chart.containers.axisLinesconatiner.Refresh();
        }

        protected virtual bool CheckType()
        {
            return true;
        }

        public virtual void Refresh()
        {
            InvalidateVisuals();
        }

        internal void RefreshSeries()
        {
            if (Series != null)
                foreach (BaseSeries series in this.Series)
                {
                    series.RefreshWithoutAxis(this);
                }
        }
        internal double m_Interval;
        internal double m_MaxValue = 1;
        internal double m_MinValue = 0;
        //internal double IntervalCount = 5;
        internal List<string> m_Labels;
        internal double m_offset = 0;
        internal double m_startValue = 0;
        bool isStartSet;



        public double IntervalCount
        {
            get { return (double)GetValue(IntervalCountProperty); }
            set { SetValue(IntervalCountProperty, value); }
        }
        public static readonly DependencyProperty IntervalCountProperty = DependencyProperty.Register("IntervalCount", typeof(double), typeof(AxisBase), new PropertyMetadata(5d));




        /// <summary>
        /// String Format for AxisLabel
        /// </summary>
        public string StringFormat
        {
            get { return (string)GetValue(StringFormatProperty); }
            set { SetValue(StringFormatProperty, value); }
        }
        public static readonly DependencyProperty StringFormatProperty = DependencyProperty.Register("StringFormat", typeof(string), typeof(AxisBase), new PropertyMetadata(string.Empty));



        /// <summary>
        /// Minor Line Size
        /// </summary>
        public double MinorLineSize
        {
            get { return (double)GetValue(MinorLineSizeProperty); }
            set { SetValue(MinorLineSizeProperty, value); }
        }
        public static readonly DependencyProperty MinorLineSizeProperty = DependencyProperty.Register("MinorLineSize", typeof(double), typeof(AxisBase), new PropertyMetadata(6d));


        /// <summary>
        /// Major Line Size
        /// </summary>
        public double MajorLineSize
        {
            get { return (double)GetValue(MajorLineSizeProperty); }
            set { SetValue(MajorLineSizeProperty, value); }
        }
        public static readonly DependencyProperty MajorLineSizeProperty = DependencyProperty.Register("MajorLineSize", typeof(double), typeof(AxisBase), new PropertyMetadata(10d));


        /// <summary>
        /// Major Tick Line Visible only if ShowMajorTicks is set to True
        /// </summary>
        public bool ShowMajorTicks
        {
            get { return (bool)GetValue(ShowMajorTicksProperty); }
            set { SetValue(ShowMajorTicksProperty, value); }
        }
        public static readonly DependencyProperty ShowMajorTicksProperty = DependencyProperty.Register("ShowMajorTicks", typeof(bool), typeof(AxisBase), new PropertyMetadata(true));


        /// <summary>
        /// 
        /// </summary>
        public int MinorTicksCount
        {
            get { return (int)GetValue(MinorTicksCountProperty); }
            set { SetValue(MinorTicksCountProperty, value); }
        }
        public static readonly DependencyProperty MinorTicksCountProperty = DependencyProperty.Register("MinorTicksCount", typeof(int), typeof(AxisBase), new PropertyMetadata(0));



        public object Header
        {
            get { return (object)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }
        public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(object), typeof(AxisBase), new PropertyMetadata(null));


        public Chart Chart
        {
            get { return (Chart)GetValue(ChartProperty); }
            set { SetValue(ChartProperty, value); }
        }
        public static readonly DependencyProperty ChartProperty = DependencyProperty.Register("Chart", typeof(Chart), typeof(AxisBase), new PropertyMetadata(null));


        public bool ShowCrossLines
        {
            get { return (bool)GetValue(ShowCrossLinesProperty); }
            set { SetValue(ShowCrossLinesProperty, value); }
        }
        public static readonly DependencyProperty ShowCrossLinesProperty = DependencyProperty.Register("ShowCrossLines", typeof(bool), typeof(AxisBase), new PropertyMetadata(true));


        public bool ShowMinorCrossLines
        {
            get { return (bool)GetValue(ShowMinorCrossLinesProperty); }
            set { SetValue(ShowMinorCrossLinesProperty, value); }
        }
        public static readonly DependencyProperty ShowMinorCrossLinesProperty = DependencyProperty.Register("ShowMinorCrossLines", typeof(bool), typeof(AxisBase), new PropertyMetadata(true));


        public CollectionSeries Series
        {
            get { return (CollectionSeries)GetValue(SeriesProperty); }
            set { SetValue(SeriesProperty, value); }
        }
        public static readonly DependencyProperty SeriesProperty = DependencyProperty.Register("Series", typeof(CollectionSeries), typeof(AxisBase), new PropertyMetadata(null));
    }
}
